﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Irrlicht.Net;
using Irrlicht.Net.Core;
using Irrlicht.Net.GUI;
using Irrlicht.Net.IO;
using Irrlicht.Net.Scene;
using Irrlicht.Net.Video;

namespace Tutorial4
{
    // To receive events like mouse and keyboard input, or GUI events like "the OK button has been clicked",
    // we need an object which is derived from the IEventReceiver object. There is only one method to 
    // override: IEventReceiver.OnEvent(). This method will be called by the engine once when an event 
    // happens. What we really want to know is whether a key is being held down, and so we will remember the
    // current state of each key. 

    public class MyEventReceiver : EventReceiver, IEventReceiver
    {
        // This is the one method that we have to implement
        public bool OnEvent(IrrEventArgs e)
        {
            // Remember whether each key is down or up
            if (e.EventType == EventTypes.KeyInput)
            {
                KeyIsDown[(int)e.KeyInput.Key] = e.KeyInput.PressedDown;
            }

            return false;
        }

        // This is used to check whether a key is being held down
        public bool IsKeyDown(KeyCodes keyCode) 
        {
            if (keyCode > KeyCodes.Count)
            {
                return false;
            }

            return KeyIsDown[(uint)keyCode];
        }
        
        public MyEventReceiver()
        {
            for (uint i = 0; i < (uint)KeyCodes.Count; ++i)
            {
                KeyIsDown[i] = false;
            }

            this.IrrEvent += new IrrEventHandler(OnEvent);
        }


        // We use this array to store the current state of each key
        private bool[] KeyIsDown = new bool[(int)KeyCodes.Count];
    };

    class Program
    {
        static int Main(string[] args)
        {
            // The event receiver for keeping the pressed keys is ready, the actual responses will be made inside
            // the render loop, right before drawing the scene. So lets just create an irr::IrrlichtDevice and the
            // scene node we want to move. We also create some other additional scene nodes, to show that there
            // are also some different possibilities to move and animate scene nodes. 

            // ask user for driver
            DriverTypes driverType = DriverChoice.FromConsole();

            if (driverType == DriverTypes.Count)
            {
                return 1;
            }
        
            // create device and exit if creation failed
            Device device = new Device(driverType, new Dimension2D(640, 480), 16, false, false, false);

            if (device == null)
            {
                // could not create selected driver.
                return 1;
            }

            // Create the event receiver.        
            MyEventReceiver receiver = new MyEventReceiver();

            string mediaPath = "../../../../../media/";

            // Get a pointer to the video driver and the SceneManager so that we do not
            // always have to call Device.VideoDriver and Device.SceneManager. 
            VideoDriver driver = device.VideoDriver;
            SceneManager scene = device.SceneManager;

            // Create the node which will be moved with the WSAD keys. We create a sphere node, which 
            // is a built-in geometry primitive. We place the node at (0,0,30) and assign a texture to 
            // it to let it look a little bit more interesting. Because we have no dynamic lights in this
            // scene we disable lighting for each model (otherwise the models would be black).        
            SceneNode node = scene.AddSphereSceneNode();        
            if (node != null)        
            {
                node.Position = new Vector3D(0.0F, 0.0F, 30.0F);
                node.SetMaterialTexture(0, driver.GetTexture(mediaPath + "wall.bmp"));
                node.SetMaterialFlag(MaterialFlags.Lighting, false);        
            }

            // Now we create another node, movable using a scene node animator. Scene node animators modify
            // scene nodes and can be attached to any scene node like mesh scene nodes, billboards, lights and
            // even camera scene nodes. Scene node animators are not only able to modify the position of a scene
            // node, they can also animate the textures of an object for example. We create a cube scene node and
            // attach a 'fly circle' scene node animator to it, letting this node fly around our sphere scene node. 
            SceneNode n = scene.AddCubeSceneNode();        
            if (n != null)        
            {
                n.SetMaterialTexture(0, driver.GetTexture(mediaPath + "t351sml.jpg"));
                n.SetMaterialFlag(MaterialFlags.Lighting, false);

                SceneNodeAnimator anim = scene.CreateFlyCircleAnimator(new Vector3D(0.0f, 0.0f, 30.0f), 20.0f);
                if (anim != null)
                {
                        n.AddAnimator(anim);
                        anim.Dispose();
                }
            }

            // The last scene node we add to show possibilities of scene node animators is a b3d model,
            // which uses a 'fly straight' animator to run between to points.
            AnimatedMeshSceneNode anms = scene.AddAnimatedMeshSceneNode(scene.GetMesh(mediaPath + "ninja.b3d"));
            if (anms != null)
            {
                SceneNodeAnimator anim = scene.CreateFlyStraightAnimator(new Vector3D(100.0f, 0.0f, 60.0f), new Vector3D(-100.0f, 0.0f, 60.0f), 3500, true);
                if (anim != null)
                {
                    anms.AddAnimator(anim);
                    anim.Dispose();
                }
            
                // To make the model look right we disable lighting, set the frames between which the animation should loop,
                // rotate the model around 180 degrees, and adjust the animation speed and the texture. To set the right
                // animation (frames and speed), we would also be able to just call "anms.Animation = AnimationTypes.Run"
                // for the 'run' animation instead of "setFrameLoop" and "setAnimationSpeed", but this only works with MD2
                // animations, and so you know how to start other animations. But a good advice is to not use hardcoded frame-numbers... 
                anms.SetMaterialFlag(MaterialFlags.Lighting, false);

                anms.SetFrameLoop(0, 13);
                anms.AnimationSpeed = 15.0f;
                ////anms.Animation = AnimationTypes.Run;

                anms.Scale = new Vector3D(2.0f, 2.0F, 2.0f);
                anms.Rotation = new Vector3D(0.0f, -90.0f, 0.0f);
                ////anms.SetMaterialTexture(0, driver.GetTexture(mediaPath + "sydney.bmp"));        
            }

            // To be able to look at and move around in this scene, we create a first person shooter
            // style camera and make the mouse cursor invisible.         
            scene.AddCameraSceneNodeFPS();        
            device.CursorControl.Visible = false;

            // Add a colorful irrlicht logo         
            device.GUIEnvironment.AddImage(
                driver.GetTexture(mediaPath + "irrlichtlogoalpha2.tga"),
                new Position2D<int>(10, 20));

            StaticText diagnostics = device.GUIEnvironment.AddStaticText(string.Empty, new Rectangle<int>(10, 10, 400, 20));        
            diagnostics.OverrideColor = new Color(255, 255, 255, 0);

            // We have done everything, so lets draw it. We also write the current frames per second
            // and the name of the driver to the caption of the window.         
            int lastFPS = -1;

            // In order to do framerate independent movement, we have to know
            // how long it was since the last frame
            uint then = device.Timer.GetTime();
        
            // This is the movement speed in units per second.
            const float MOVEMENT_SPEED = 5.0F;
      
            while(device.Run())        
            {
                // Work out a frame delta time.
                uint now = device.Timer.GetTime();
                float frameDeltaTime = (float)(now - then) / 1000.0F; // Time in seconds
                then = now;

                // Check if keys W, S, A or D are being held down, and move the sphere node around respectively. 
                Vector3D nodePosition = node.Position;

                if (receiver.IsKeyDown(KeyCodes.KeyW))
                {
                    nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
                }
                else if (receiver.IsKeyDown(KeyCodes.KeyS))
                {
                    nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
                }

                if (receiver.IsKeyDown(KeyCodes.KeyA))
                {
                    nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
                }
                else if (receiver.IsKeyDown(KeyCodes.KeyD))
                {
                    nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
                }
                
                node.Position = nodePosition;

                driver.BeginScene(true, true, new Color(255, 113, 113, 133));

                scene.DrawAll(); // draw the 3D scene
                device.GUIEnvironment.DrawAll(); // draw the gui environment (the logo)

                driver.EndScene();

                int fps = driver.FPS;

                if (lastFPS != fps)
                {
                        string tmp = string.Format("Movement Example - Irrlicht Engine [{0}] fps: {1:D}", driver.Name, fps);
                        device.WindowCaption = tmp;
                        lastFPS = fps;
                }
            }

            // In the end, delete the Irrlicht device. 
            device.Dispose();
            return 0;
        }
    }
}
